package indi.h.mlx.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import indi.h.mlx.analysises.ClassAnalyser;
import indi.h.mlx.analysises.inter.Analyser;
import indi.h.mlx.core.bean.Column;
import indi.h.mlx.core.bean.Table;
import indi.h.mlx.custom.analysises.CFieldAnalysis;
import indi.h.mlx.custom.analysises.CMethodAnalysis;
import indi.h.mlx.custom.analysises.CTableAnalysis;
import indi.h.mlx.sql.SQLJoinImp;
import indi.h.mlx.sql.SQLStructureImp;
import indi.h.mlx.sql.inter.SQLJoin;
import indi.h.mlx.sql.inter.SQLStructure;
import indi.h.mlx.sql.items.SQLData;

public class UseAnn implements AnnotationUse {

	protected Object object;

	protected Analyser<Class<?>, Table> analyser;

	protected UseAnn() {
		this.analyser = new ClassAnalyser(new CTableAnalysis<>(), new CMethodAnalysis(), new CFieldAnalysis());
	}

	public static AnnotationUse instance(Object obj) {
		// TODO Auto-generated method stub
		UseAnn au = new UseAnn();
		au.object = obj;
		return au;
	}

	@Override
	public SQLStructure toSql() {
		// TODO Auto-generated method stub

		Table work = this.analyser.work(object.getClass());
		work.setFieldName("");

		SQLStructureImp ssi = new SQLStructureImp();
		// 表明关联
		Map<String, SQLJoin> m = new HashMap<>();
		m.put("", new SQLJoinImp(work.getTable().name(), null));
		// 数据关联
		List<SQLData> allData = new ArrayList<>();
		// 字段关联
		Map<String, Column> allColumn = new HashMap<>();

		ssi.setRootTable(work.getTable().name());

		ssi.setTableMap(m);
		ssi.setAllData(allData);
		ssi.setColumnName(allColumn);

		ssi.setIdData(columnToSqlData(work.getIds(), this.object, allData, allColumn, work.getTable().name()));
		ssi.setRootData(columnToSqlData(work.getColumns(), this.object, allData, allColumn, work.getTable().name()));
		List<SQLData> columnToSqlData = columnToSqlData(work.getJoins(), this.object, allData, allColumn,
				work.getTable().name());
		ssi.setJoinData(columnToSqlData);

		listJoin(work.getJoins(), columnToSqlData, this.object, allData, allColumn, m);

		return ssi;
	}

	private void listJoin(List<Column> l, List<SQLData> join, Object o, List<SQLData> allData,
			Map<String, Column> allColumn, Map<String, SQLJoin> m) {
		listJoin(l, join, o, allData, allColumn, m, new HashMap<>(), null);
	}

	private void listJoin(List<Column> l, List<SQLData> join, Object o, List<SQLData> allData,
			Map<String, Column> allColumn, Map<String, SQLJoin> m, Map<String, Integer> nameIndex, ArrayData ad) {
		for (Column c : l) {
			try {
				
				Object object2 = null;
				if (o != null)
					object2=c.getDataInter().get(o);

				Class<?> type = c.getDataInter().type();
				Table work = this.analyser.work(type);
				work.setFieldName(c.getFieldName());

				ArrayData arrayData = new ArrayData(ad, object2, work.getIds(), type);

				if (testArray(arrayData, arrayData)) {
					join.stream().forEach((j) -> {
						j.setValue(null);
					});
					return;
				}
				
				resetJoinData(c, join, allColumn, object2, work.getIds());

				Integer integer = nameIndex.get(work.getTable().name());
				if (integer == null) {
					nameIndex.put(work.getTable().name(), 1);
					m.put(work.getFieldName()[0], new SQLJoinImp(work.getTable().name(), null));
				} else {
					nameIndex.put(work.getTable().name(), integer + 1);
					m.put(work.getFieldName()[0],
							new SQLJoinImp(work.getTable().name(), work.getTable().name() + integer));
				}

				columnToSqlData(work.getIds(), object2, allData, allColumn, work.getTable().name());
				columnToSqlData(work.getColumns(), object2, allData, allColumn, work.getTable().name());
				List<SQLData> columnToSqlData = columnToSqlData(work.getJoins(), object2, allData, allColumn,
						work.getTable().name());

				listJoin(work.getJoins(), columnToSqlData, object2, allData, allColumn, m, nameIndex, arrayData);

			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}

		}

	}

	private void resetJoinData(Column c, List<SQLData> join, Map<String, Column> allColumn, Object obj,
			List<Column> ids) {
		for (SQLData j : join) {
			Column column = allColumn.get(j.getFieldName());
			String joinColumn = column.getcColumn().joinColumn();
			String name = column.getcColumn().name();
			if (j.getFieldName().equals(c.completeFieldName()))
				for (Column tc : ids) {
					try {
						if (joinColumn != null && tc.getFieldName().equals(joinColumn)) {

							j.setValue(tc.getDataInter().get(obj));

							return;
						}
						if (tc.getcColumn().name().equals(name)) {
							j.setValue(tc.getDataInter().get(obj));

							return;
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
		}
	}

	private boolean testArray(ArrayData a, ArrayData b) {
		if (a.ad == null)
			return false;
		if (a.ad.compareTo(b) == 1) {
			return true;
		} else if (testArray(a.ad, b)) {
			return true;
		}
		return false;
	}

	class ArrayData implements Comparable<ArrayData> {
		ArrayData ad;
		Object object;
		List<Column> ids;
		Class<?> ct;

		public ArrayData(ArrayData ad, Object object, List<Column> ids, Class<?> ct) {
			this.ad = ad;
			this.object = object;
			this.ids = ids;
			this.ct = ct;
		}

		@Override
		public int compareTo(ArrayData o) {
			// TODO Auto-generated method stub
			if (o == null)
				return 0;
			if (o.ct != this.ct)
				return 0;
			if (o.object == null && this.object == null)
				return 1;
			if (o.object == null && this.object != null)
				return 1;

			for (Column c : this.ids) {
				try {
					Object object2 = c.getDataInter().get(o.object);
					Object object3 = c.getDataInter().get(this.object);
					if (!object2.equals(object3)) {
						return 0;
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return 1;
		}

	}

	private List<SQLData> columnToSqlData(List<Column> l, Object o, List<SQLData> allData,
			Map<String, Column> allColumn, String tableName) {
		List<SQLData> sl = new ArrayList<>();
		l.stream().forEach((c) -> {
			allColumn.put(c.completeFieldName(), c);
			try {
				if (o == null)
					return;
				Object value = c.getDataInter().get(o);
				if (value != null)
					sl.add(new SQLData(c.getcColumn().name(), value, tableName, c.completeFieldName(),
							c.getcColumn().selectType().get(), c.getcColumn().singleNexus().get(),
							c.getcColumn().mulitNexus().get(), c.getcColumn().separator()));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		});
		allData.addAll(sl);
		return sl;
	}

}
